#ifndef __ARRAY_BUFFER_H__
#define __ARRAY_BUFFER_H__

#include <stdexcept>

#ifndef ASSERT
	#ifdef _DEBUG
		#include <assert.h>
		#define ASSERT(x) assert(x)
	#else
		#define ASSERT(x) 
	#endif
#endif

template<class T, size_t BufferSize = 1024>
class ArrayBuffer
{
private:
	T _buffer[BufferSize];
	size_t _pointer;

public:
	ArrayBuffer() : _pointer(0) {}

	const T& operator[](size_t idx) const {
		ASSERT(idx >= 0 && idx < size() && "Access out of range");
		return _buffer[idx];
	}

	T& operator[](size_t idx) {
		ASSERT(idx >= 0 && idx < size() && "Access out of range");
		return _buffer[idx];
	}
	
	const T& at(size_t idx) const {
		if (idx < 0 || idx > size()) {
			throw std::out_of_range("invalid CyclicBuffer at()");
		}
		return _buffer[idx];
	}

	T& at(size_t idx) {
		if (idx < 0 || idx > size()) {
			throw std::out_of_range("invalid CyclicBuffer at()");
		}
		return _buffer[idx];
	}

	T* pointer() {
		return _buffer;
	}
	const T* pointer() const {
		return _buffer;
	}
	size_t size() const { return _pointer; }
	size_t capacity() const { return BufferSize; }
	bool empty() const { return _pointer == 0; }
	bool full() const { return size() >= capacity(); }
	void clear() { _pointer = 0; }

	T& front() {
		ASSERT(!empty() && "Access empty");
		return _buffer[0];
	}

	const T& front() const{
		ASSERT(!empty() && "Access empty");
		return _buffer[0];
	}

	T& back() {
		ASSERT(!empty() && "Access empty");
		return _buffer[_pointer - 1];
	}

	const T& back() const {
		ASSERT(_pointer && "Access empty");
		return _buffer[_pointer - 1];
	}

	void push_back(const T& value) {
		ASSERT(size() < capacity() && "Push out of range");
		_buffer[_pointer] = value;
		_pointer++;
	}

	void pop_back() {
		ASSERT(!empty() && "Pop empty");
		_pointer--;
	}

	template<class internal_T>
	class _ArrayBuffer_const_iterator
	{
		typedef _ArrayBuffer_const_iterator<internal_T> _my_t;
		typedef ArrayBuffer<internal_T, BufferSize> _container_t;
		internal_T* _pointer;
		_container_t* _container;
	public:
		_ArrayBuffer_const_iterator() { }

		_ArrayBuffer_const_iterator(internal_T* ptr, _container_t* containerPtr) {
			_pointer = ptr;
			_container = containerPtr;
		}

		explicit _ArrayBuffer_const_iterator(internal_T* ptr) {
			_pointer = ptr;
			_container = 0;
		}
		const internal_T& operator*() const {
			return (*_pointer);
		}

		const internal_T* operator->() const {
			return (&**this);
		}

		bool operator==(const _my_t& right) const {
			ASSERT(_container == right._container && "not same container");
			return (_pointer == right._pointer);
		}

		bool operator!=(const _my_t& right) const {
			ASSERT(_container == right._container && "not same container");
			return (!(*this == right));
		}

		_my_t& operator++() {
			++_pointer;
			return (*this);
		}

		_my_t operator++(int) {
			_my_t result = *this;
			++*this;
			return (result);
		}

		_my_t& operator--() {
			--_pointer;
			return (*this);
		}

		_my_t operator--(int) {
			_my_t result = *this;
			--*this;
			return (result);
		}
#ifdef COMPLEX_ITERATOR
		_my_t& operator+=(ptrdiff_t off) {
			_pointer += off;
			return (*this);
		}

		_my_t operator+(ptrdiff_t off) const {
			_my_t result = *this;
			return (result += off);
		}

		_my_t& operator-=(ptrdiff_t off) {
			return (*this += -off);
		}

		_my_t operator-(ptrdiff_t off) const
		{	// return this - integer
			_my_t result = *this;
			return (result -= off);
		}

		ptrdiff_t operator-(const _my_t& right) const {
			ASSERT(_container == right._container && "not same container");
			return (_pointer - right._pointer);
		}

		internal_T& operator[](ptrdiff_t off) const {
			return (*(*this + off));
		}

		bool operator<(const _my_t& right) const {
			ASSERT(_container == right._container && "not same container");
			return (_pointer < right._pointer);
		}

		bool operator>(const _my_t& right) const {
			ASSERT(_container == right._container && "not same container");
			return (right < *this);
		}

		bool operator<=(const _my_t& right) const {
			ASSERT(_container == right._container && "not same container");
			return (!(right < *this));
		}

		bool operator>=(const _my_t& right) const {
			ASSERT(_container == right._container && "not same container");
			return (!(*this < right));
		}
	};

	template<class internal_T>
	inline _ArrayBuffer_const_iterator<internal_T> operator+(ptrdiff_t off, _ArrayBuffer_const_iterator<internal_T> next) {
		return (next += off);
	}
#else //COMPLEX_ITERATOR
	};
#endif //COMPLEX_ITERATOR

	template<class internal_T>
	class _ArrayBuffer_iterator : public _ArrayBuffer_const_iterator<T>
	{
		typedef _ArrayBuffer_iterator<internal_T> _my_t;
		typedef ArrayBuffer<internal_T, BufferSize> _container_t;
		typedef _ArrayBuffer_const_iterator<internal_T> _my_base_t;
	public:
		_ArrayBuffer_iterator() { }

		_ArrayBuffer_iterator(internal_T* ptr, _container_t * containerPtr)
			: _my_base_t(ptr, containerPtr) { }

		explicit _ArrayBuffer_iterator(internal_T* ptr)
			: _my_base_t(ptr) { }

		internal_T& operator*() const {
			return ((internal_T&)**(_my_base_t *)this);
		}

		internal_T* operator->() const {
			return (&**this);
		}

		_my_t& operator++() {
			++(*(_my_base_t *)this);
			return (*this);
		}

		_my_t operator++(int) {
			_my_t result = *this;
			++*this;
			return (result);
		}

		_my_t& operator--() {
			--(*(_my_base_t *)this);
			return (*this);
		}

		_my_t operator--(int) {
			_my_t result = *this;
			--*this;
			return (result);
		}

#ifdef COMPLEX_ITERATOR
		_my_t& operator+=(ptrdiff_t off) {
			(*(_my_base_t *)this) += off;
			return (*this);
		}

		_my_t operator+(ptrdiff_t off) const {
			_my_t result = *this;
			return (result += off);
		}

		_my_t& operator-=(ptrdiff_t off) {
			return (*this += -off);
		}

		_my_t operator-(ptrdiff_t off) const {
			_my_t result = *this;
			return (result -= off);
		}

		ptrdiff_t operator-(const _my_base_t& right) const
		{	// return difference of iterators
			return (*(_my_base_t *)this - right);
		}

		internal_T& operator[](ptrdiff_t off) const {
			return (*(*this + off));
		}
	};

	template<class internal_T>
	inline _ArrayBuffer_iterator<internal_T> operator+(ptrdiff_t off, _ArrayBuffer_iterator<internal_T> next) {
		return (next += off);
	}
#else //COMPLEX_ITERATOR
	};
#endif //COMPLEX_ITERATOR
	typedef _ArrayBuffer_const_iterator<T> const_iterator;
	typedef _ArrayBuffer_iterator<T> iterator;

	typedef std::reverse_iterator<iterator> reverse_iterator;
	typedef std::reverse_iterator<const_iterator> const_reverse_iterator;

#ifdef _DEBUG
	iterator begin() {
		return (iterator(_buffer, this));
	}

	const_iterator begin() const
	{	// return iterator for beginning of nonmutable sequence
		return (const_iterator(_buffer, this));
	}

	iterator end()
	{	// return iterator for end of mutable sequence
		return (iterator(_buffer + _pointer, this));
	}

	const_iterator end() const
	{	// return iterator for end of nonmutable sequence
		return (const_iterator(_buffer + _pointer, this));
	}
#else /* _DEBUG */
	iterator begin()
	{	// return iterator for beginning of mutable sequence
		return (iterator(_buffer));
	}

	const_iterator begin() const
	{	// return iterator for beginning of nonmutable sequence
		return (const_iterator(_buffer));
	}

	iterator end()
	{	// return iterator for end of mutable sequence
		return (iterator(_buffer + _pointer));
	}

	const_iterator end() const
	{	// return iterator for end of nonmutable sequence
		return (const_iterator(_buffer + _pointer));
	}
#endif /* _DEBUG */
	reverse_iterator rbegin() {
		return (reverse_iterator(end()));
	}

	const_reverse_iterator rbegin() const {
		return (const_reverse_iterator(end()));
	}

	reverse_iterator rend() {
		return (reverse_iterator(begin()));
	}

	const_reverse_iterator rend() const {
		return (const_reverse_iterator(begin()));
	}
	iterator erase(iterator where) {
		*where = back();
		pop_back();
		return iterator(where);
	}
};

#endif //__ARRAY_BUFFER_H__